﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace gears
{
    class GearWheel
    {

        private List<Tooths> tooths = new List<Tooths>();
        private Vector2 gear_wheel_position;
        private float rotate_speed = 0;
        private float gear_wheel_angle = 0;
        private Texture2D gear_wheel_sprite;
        private Texture2D smallGearSprite;
        private Texture2D tooth_sprite;
        private float radius = 0;
        private float smallRadius = 20;
        private float teetNumber = 0;
        private bool driver = false;
        private bool drived = true;
        private bool visible = true;
        private bool isFixed = false;
        private bool handDrived = false;
        private bool binded = false;
        private bool tapped = false;
        private float rotated = 0;
        private float tapPos = 0;
        private float prevTapPos = -1;
        private Axis axis = null;

        //  constructor
        public GearWheel(Vector2 _gear_wheel_postion, int _teet, float _rotate_speed, Texture2D _gear_wheel_sprite, Texture2D _tooth_sprite, Texture2D _smallGearSprite)
        {
            float angle = 0;
            teetNumber = (float)_teet;
            rotate_speed = _rotate_speed;
            smallGearSprite = _smallGearSprite;
            radius = Convert.ToInt32((_tooth_sprite.Width * 2 * _teet) / (2*Math.PI));

            float angle_distinct = (float)(Math.PI*2) / _teet;

            for (int i = 0; i < _teet; i++)
            {
                tooths.Add(new Tooths(_gear_wheel_postion, angle, rotate_speed));
                angle += angle_distinct;
            }

            gear_wheel_sprite = _gear_wheel_sprite;
            tooth_sprite = _tooth_sprite;
            gear_wheel_position = _gear_wheel_postion;
        }
       
        public List<Tooths> ToothsList
        {
            get
            {
                return tooths;
            }
        }
        public Texture2D GearWheelSprite
        {
            get
            {
                return gear_wheel_sprite;
            }
        }
        public Texture2D ToothSprite
        {
            get
            {
                return tooth_sprite;
            }
        }
        public bool Driver
        {
            get
            {
                return driver;
            }
            set
            {
                driver = value;
            }
        }
        public Vector2 GearWheelPostion
        {
            get
            {
                return gear_wheel_position;
            }
            set
            {
                gear_wheel_position = value;

                foreach (Tooths tooth in tooths)
                {
                    tooth.ToothPosition = gear_wheel_position;
                }
            }
        }
        public float GearWheelAngle
        {
            get
            {
                return gear_wheel_angle;
            }
            set
            {
                gear_wheel_angle = value;
            }
        }
        public bool Binded
        {
            get
            {
                return binded;
            }
            set
            {
                binded = value;
            }
        }
        public bool Tapped
        {
            get
            {
                return tapped;
            }
            set
            {
                tapped = value;
            }
        }
        public bool IsFixed
        {
            get
            {
                return isFixed;
            }
            set
            {
                isFixed = value;
                if (isFixed)
                    binded = true;
            }
        }
        public Axis Axis
        {
            get
            {
                return axis;
            }
            set
            {
                axis = value;
            }
        }
        public bool HandDrived
        {
            get
            {
                return handDrived;
            }
            set
            {
                handDrived = value;
                if (handDrived)
                {
                    isFixed = true;
                    binded = true;
                }
            }
        }
        long prev_time; 
        public void Rotate()
        {

            long time = Convert.ToInt64(DateTime.Now.ToString("0MMddyyHmmssfff"));


            gear_wheel_angle += rotate_speed;

            foreach (Tooths _tooth in tooths)
            {
                _tooth.RotateTooth();
            }
            if (axis != null && handDrived == false && rotate_speed != 0)
            {
                if (time - prev_time > 900)
                {
                    Game1.mySoundEffects.Play("gear");
                    prev_time = time;
                }
            }
           
        }
        public float Rotated
        {
            get
            {
                return rotated;
            }
            set
            {
                rotated = value;
            }
        }
        public bool Drived
        {
            get
            {
                return drived;
            }
            set
            {
                drived = value;
                driver = !drived;
            }
        }
        public float Radius
        {
            get
            {
                return radius;
            }
        }
        public float DistanceFromPoint(Vector2 point)
        {
            return (float)Math.Sqrt((double)Math.Pow((double)(gear_wheel_position.X - point.X), 2) + 
                                            Math.Pow((double)(gear_wheel_position.Y - point.Y), 2));
        }
        public float TapPosition
        {
            get
            {
                return tapPos;
            }
            set
            {
                tapPos = value;
            }
        }
        public float SmallRadious
        {
            get 
            {
                return smallRadius;
            }
            set
            {
                smallRadius = value;
            }
        }
        public bool Visible
        {
            get
            {
                return visible;
            }
            set
            {
                visible = value;
            }
        }

        public float AngleToPoint(Vector2 point)
        {
            float y = point.Y - gear_wheel_position.Y;
            float x = point.X - gear_wheel_position.X;
            double angle =Math.Atan2(y, x);
            if (angle < 0)
                angle += Math.PI * 2;
            return (float)angle;
        }

        public void Update()
        {
            if (!binded)
                return;
            if (driver)
            {                
                rotated = rotate_speed;
            }
            else
            {
                if (handDrived && tapped)
                {
                    if (prevTapPos >= 0)
                    {
                        if(Math.Abs(tapPos - prevTapPos) < Math.PI )
                            rotate_speed = tapPos - prevTapPos;
                        prevTapPos = tapPos;
                        rotated = rotate_speed;
                    }
                    else
                        prevTapPos = tapPos;                   
                }
                else if(drived)
                {
                    rotate_speed = rotated;
                    
                    prevTapPos = -1;
                }
                foreach (Tooths _tooth in tooths)
                {
                    _tooth.ToothSpeed = this.rotate_speed;
                }                
            }
            if (axis != null)
            {
                axis.RotationChange = rotated;
            }
            Rotate();
            if(!(handDrived && tapped))
                rotated = 0;
            tapped = false;
        }

        public void SetTeethToAngle(float angle)
        {           
            int i = 0;
            float angleBetweenTeeth = (float)Math.PI * 2 / teetNumber;
            if (angle < 0)
                angle = (angle * -1) + (float)Math.PI;
            //if (angle == 0)
              //  angle = angleBetweenTeeth * 2;
            foreach (Tooths _tooth in tooths)
            {
                _tooth.ToothSpeed = this.rotate_speed;
                if (_tooth.ToothAngle > angle)
                    break;
                i++;
            }
            if (i > 0)
                i--;
            float rotateAngle = angle - tooths[i].ToothAngle;
            float rs = rotate_speed;
            rotate_speed = rotateAngle;
            foreach (Tooths _tooth in tooths)
            {
                _tooth.ToothSpeed = rotate_speed;
            }
            Rotate();
            rotate_speed = rs;
        }

        public void SetSpaceToAngle(float angle)
        {
            float angleBetweenTeeth = (float)Math.PI * 2 / (tooths.Count-1);
            int i = 0;
            if (angle < 0)
                angle = (angle * -1) + (float)Math.PI;
            foreach (Tooths _tooth in tooths)
            {
                _tooth.ToothSpeed = this.rotate_speed;
                if (_tooth.ToothAngle > angle)
                    break;
                i++;
            }
            if (i > 0)
                i--;
            float rotateAngle = angle - tooths[i].ToothAngle + angleBetweenTeeth / 2;
            
            //float rotateAngle = angle - tooths[i].ToothAngle;
            float rs = rotate_speed;
            rotate_speed = rotateAngle;
            foreach (Tooths _tooth in tooths)
            {
                _tooth.ToothSpeed = rotate_speed;
            }
            Rotate();
            rotate_speed = rs;
        }

        public bool CheckCollisions(GearWheel gw2)
        {
            bool collide = false;
            
            Vector2 p = gw2.gear_wheel_position;
            float distance = DistanceFromPoint(p);

            if (distance < (radius + gw2.Radius) + tooth_sprite.Height * 1.6f)
            {
                Collide(gw2);
                collide = true;
            }
            return collide;
        }

        public void Collide(GearWheel gw)
        {
            rotated = gw.rotated * (gw.teetNumber / this.teetNumber) * -1;
        }

        public void Draw(SpriteBatch spritebatch)
        {
            if (!visible)
                return;
            if (binded || tapped)
            {
                foreach (Tooths _tooth in tooths)
                {
                    spritebatch.Draw(tooth_sprite,
                                    new Rectangle((int)(_tooth.ToothPosition.X), (int)(_tooth.ToothPosition.Y), tooth_sprite.Width, tooth_sprite.Height),
                                    null,
                                    Color.White,
                                    _tooth.ToothAngle,
                                    new Vector2(0, radius + tooth_sprite.Height * 0.8f),
                                    SpriteEffects.None,
                                    0.0f);
                }

                spritebatch.Draw(gear_wheel_sprite,
                                   new Rectangle((int)gear_wheel_position.X, (int)gear_wheel_position.Y, (int)radius * 2, (int)radius * 2),
                                   null,
                                   Color.White,
                                   gear_wheel_angle,
                                   new Vector2(gear_wheel_sprite.Width / 2, gear_wheel_sprite.Height / 2),
                                   SpriteEffects.None, 0.0f);

            }
            else
            {
                spritebatch.Draw(smallGearSprite,
                                   new Rectangle((int)gear_wheel_position.X, (int)gear_wheel_position.Y, (int)smallRadius * 3, (int)smallRadius * 3),
                                   null,
                                   Color.White,
                                   gear_wheel_angle,
                                   new Vector2(smallGearSprite.Width / 2, smallGearSprite.Height / 2),
                                   SpriteEffects.None, 0.0f);
            }
        }
    }
  
    class Tooths
    {
        private Vector2 tooth_position = new Vector2(0, 0);
        private float tooth_angle = 0;
        private float angle_speed = 0;

        public Tooths(Vector2 _tooth_position, float _tooth_angle, float _angle_speed)
        {
            tooth_position = _tooth_position;
            tooth_angle = _tooth_angle;
            angle_speed = _angle_speed;

        }
        public void RotateTooth()
        {
            tooth_angle += angle_speed;
            if (tooth_angle > Math.PI * 2)
                tooth_angle = tooth_angle - (float)Math.PI * 2;
            else if (tooth_angle < 0)
                tooth_angle = tooth_angle + (float)Math.PI * 2 ;
        }

        public float ToothAngle
        {
            get
            {
                return tooth_angle;
            }
            set
            {
                tooth_angle = value;
            }
        }
        public float ToothSpeed
        {
            get
            {
                return angle_speed;
            }
            set
            {
                angle_speed = value;
            }
        }
        public Vector2 ToothPosition
        {
            get
            {
                return tooth_position;
            }
            set
            {
                tooth_position = value;
            }
        }
    }
}
